package com.cwz.security.auth;

import com.cwz.api.model.LoginUser;
import com.cwz.core.context.SecurityContextHolder;
import com.cwz.core.exception.auth.NotLoginException;
import com.cwz.core.exception.auth.NotPermissionException;
import com.cwz.core.exception.auth.NotRoleException;
import com.cwz.core.utils.spring.SpringUtils;
import com.cwz.core.utils.string.StringUtils;
import com.cwz.security.annotation.Logical;
import com.cwz.security.annotation.RequiresLogin;
import com.cwz.security.annotation.RequiresPermissions;
import com.cwz.security.annotation.RequiresRoles;
import com.cwz.security.service.TokenService;
import com.cwz.security.utils.SecurityUtils;
import org.springframework.util.PatternMatchUtils;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * @program: w-demo
 * @description: Token 权限验证，逻辑实现类
 * @author: Wen
 **/
public class AuthLogic {
	/**
	 * 所有权限标识
	 */
	private static final String ALL_PERMISSION = "*:*:*";

	/**
	 * 管理员角色权限标识
	 */
	private static final String SUPER_ADMIN = "admin";

	public TokenService tokenService = SpringUtils.getBean(TokenService.class);

	/**
	 * 会话注销
	 */
	public void logout() {
		String token = SecurityUtils.getToken();
		if (token == null) {
			return;
		}
		logoutByToken(token);
	}

	/**
	 * 会话注销，根据指定Token
	 */
	public void logoutByToken(String token) {
		tokenService.delLoginUser(token);
	}

	/**
	 * 检验用户是否已经登录，如未登录，则抛出异常
	 */
	public void checkLogin() {
		getLoginUser();
	}

	/**
	 * 获取当前用户缓存信息, 如果未登录，则抛出异常
	 *
	 * @return 用户缓存信息
	 */
	public LoginUser getLoginUser() {
		String token = SecurityUtils.getToken();
		if (token == null) {
			throw new NotLoginException("未提供token");
		}
		LoginUser loginUser = SecurityUtils.getLoginUser();
		if (loginUser == null) {
			throw new NotLoginException("无效的token");
		}
		return loginUser;
	}

	/**
	 * 获取当前用户缓存信息, 如果未登录，则抛出异常
	 *
	 * @param token 前端传递的认证信息
	 * @return 用户缓存信息
	 */
	public LoginUser getLoginUser(String token) {
		return tokenService.getLoginUser(token);
	}

	/**
	 * 验证当前用户有效期, 如果相差不足120分钟，自动刷新缓存
	 *
	 * @param loginUser 当前用户信息
	 */
	public void verifyLoginUserExpire(LoginUser loginUser) {
		tokenService.verifyToken(loginUser);
	}

	/**
	 * 验证用户是否具备某权限
	 *
	 * @param permission 权限字符串
	 * @return 用户是否具备某权限
	 */
	public boolean hasPermi(String permission) {
		return hasPermi(getPermiList(), permission);
	}

	/**
	 * 验证用户是否具备某权限, 如果验证未通过，则抛出异常: NotPermissionException
	 *
	 * @param permission 权限字符串
	 * @return 用户是否具备某权限
	 */
	public void checkPermi(String permission) {
		if (!hasPermi(getPermiList(), permission)) {
			throw new NotPermissionException(permission);
		}
	}

	/**
	 * 根据注解(@RequiresPermissions)鉴权, 如果验证未通过，则抛出异常: NotPermissionException
	 *
	 * @param requiresPermissions 注解对象
	 */
	public void checkPermi(RequiresPermissions requiresPermissions) {
		SecurityContextHolder.setPermission(StringUtils.join(requiresPermissions.value(), ","));
		if (requiresPermissions.logical() == Logical.AND) {
			checkPermiAnd(requiresPermissions.value());
		} else {
			checkPermiOr(requiresPermissions.value());
		}
	}

	/**
	 * 验证用户是否含有指定权限，必须全部拥有
	 *
	 * @param permissions 权限列表
	 */
	public void checkPermiAnd(String... permissions) {
		Set<String> permissionList = getPermiList();
		for (String permission : permissions) {
			if (!hasPermi(permissionList, permission)) {
				throw new NotPermissionException(permission);
			}
		}
	}

	/**
	 * 验证用户是否含有指定权限，只需包含其中一个
	 *
	 * @param permissions 权限码数组
	 */
	public void checkPermiOr(String... permissions) {
		Set<String> permissionList = getPermiList();
		for (String permission : permissions) {
			if (hasPermi(permissionList, permission)) {
				return;
			}
		}
		if (permissions.length > 0) {
			throw new NotPermissionException(permissions);
		}
	}

	/**
	 * 判断用户是否拥有某个角色
	 *
	 * @param role 角色标识
	 * @return 用户是否具备某角色
	 */
	public boolean hasRole(String role) {
		return hasRole(getRoleList(), role);
	}

	/**
	 * 判断用户是否拥有某个角色, 如果验证未通过，则抛出异常: NotRoleException
	 *
	 * @param role 角色标识
	 */
	public void checkRole(String role) {
		if (!hasRole(role)) {
			throw new NotRoleException(role);
		}
	}

	/**
	 * 根据注解(@RequiresRoles)鉴权
	 *
	 * @param requiresRoles 注解对象
	 */
	public void checkRole(RequiresRoles requiresRoles) {
		if (requiresRoles.logical() == Logical.AND) {
			checkRoleAnd(requiresRoles.value());
		} else {
			checkRoleOr(requiresRoles.value());
		}
	}

	/**
	 * 验证用户是否含有指定角色，必须全部拥有
	 *
	 * @param roles 角色标识数组
	 */
	public void checkRoleAnd(String... roles) {
		Set<String> roleList = getRoleList();
		for (String role : roles) {
			if (!hasRole(roleList, role)) {
				throw new NotRoleException(role);
			}
		}
	}

	/**
	 * 验证用户是否含有指定角色，只需包含其中一个
	 *
	 * @param roles 角色标识数组
	 */
	public void checkRoleOr(String... roles) {
		Set<String> roleList = getRoleList();
		for (String role : roles) {
			if (hasRole(roleList, role)) {
				return;
			}
		}
		if (roles.length > 0) {
			throw new NotRoleException(roles);
		}
	}

	/**
	 * 根据注解(@RequiresLogin)鉴权
	 *
	 * @param at 注解对象
	 */
	public void checkByAnnotation(RequiresLogin at) {
		this.checkLogin();
	}

	/**
	 * 根据注解(@RequiresRoles)鉴权
	 *
	 * @param at 注解对象
	 */
	public void checkByAnnotation(RequiresRoles at) {
		String[] roleArray = at.value();
		if (at.logical() == Logical.AND) {
			this.checkRoleAnd(roleArray);
		} else {
			this.checkRoleOr(roleArray);
		}
	}

	/**
	 * 根据注解(@RequiresPermissions)鉴权
	 *
	 * @param at 注解对象
	 */
	public void checkByAnnotation(RequiresPermissions at) {
		String[] permissionArray = at.value();
		if (at.logical() == Logical.AND) {
			this.checkPermiAnd(permissionArray);
		} else {
			this.checkPermiOr(permissionArray);
		}
	}

	/**
	 * 获取当前账号的角色列表
	 *
	 * @return 角色列表
	 */
	public Set<String> getRoleList() {
		try {
			LoginUser loginUser = getLoginUser();
			return loginUser.getRoles();
		} catch (Exception e) {
			return new HashSet<>();
		}
	}

	/**
	 * 获取当前账号的权限列表
	 *
	 * @return 权限列表
	 */
	public Set<String> getPermiList() {
		try {
			LoginUser loginUser = getLoginUser();
			return loginUser.getPermissions();
		} catch (Exception e) {
			return new HashSet<>();
		}
	}

	/**
	 * 判断是否包含权限
	 *
	 * @param authorities 权限列表
	 * @param permission  权限字符串
	 * @return 用户是否具备某权限
	 */
	public boolean hasPermi(Collection<String> authorities, String permission) {
		// PatternMatchUtils.simpleMatch：可以根据指定字符串匹配类型格式 pattern 对字符串及字符串数组内容进行简单的查找匹配处理，从而判断是否存在。
		return authorities.stream().filter(StringUtils::hasText)
			.anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(x, permission));
	}

	/**
	 * 判断是否包含角色
	 *
	 * @param roles 角色列表
	 * @param role  角色
	 * @return 用户是否具备某角色权限
	 */
	public boolean hasRole(Collection<String> roles, String role) {
		return roles.stream().filter(StringUtils::hasText)
			.anyMatch(x -> SUPER_ADMIN.contains(x) || PatternMatchUtils.simpleMatch(x, role));
	}

}
